package fjut.acm.coding.service.Impl;

import fjut.acm.coding.component.judge.data.JudgeResultData;
import fjut.acm.coding.component.judge.data.StatusType;
import fjut.acm.coding.entity.*;
import fjut.acm.coding.entity.enums.CodeLanguage;
import fjut.acm.coding.entity.vo.JudgeStatusVO;
import fjut.acm.coding.entity.vo.LanguageUsedNumVO;
import fjut.acm.coding.entity.vo.StatusCountVO;
import fjut.acm.coding.mapper.*;

import fjut.acm.coding.service.JudgeStatusService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author axiang [2019/10/30]
 */
@Service
public class JudgeStatusServiceImpl implements JudgeStatusService {
    @Resource
    UserCustomInfoMapper userCustomInfoMapper;

    @Resource
    ProblemInfoMapper problemInfoMapper;

    @Resource
    JudgeStatusMapper judgeStatusMapper;

    @Resource
    JudgeResultMapper judgeResultMapper;

    @Resource
    UserProblemSolvedMapper userProblemSolvedMapper;

    @Resource
    UserBaseInfoMapper userBaseInfoMapper;


    @Override
    public Integer insert(JudgeStatus judgeStatus) {
        return judgeStatusMapper.insertSelective(judgeStatus);
    }

    /**
     * 如果提交成功进行的操作如下
     * 首先对题目信息表进行操作，提交成功则 +1 提交数
     * 如果是用户第一次提交则 +1 提交用户数
     * 插入一条到解题记录表中
     *
     * @param judgeStatus
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean ifSubmitSuccess(JudgeStatus judgeStatus) {
        String username = judgeStatus.getUsername();
        Integer problemId = judgeStatus.getProblemId();
        // 创建 题目信息表的检索条件
        Example exampleProbInfo = new Example(ProblemInfo.class);
        exampleProbInfo.createCriteria().andEqualTo("problemId", problemId);
        // 取得了problemId题目的数据
        ProblemInfo problemInfo = problemInfoMapper.selectOneByExample(exampleProbInfo);
        //题目ID的题目提交总数+1
        problemInfo.setTotalSubmit(problemInfo.getTotalSubmit() + 1);
        // 创建 题目解答表的检索条件
        Example exampleUserSolved = new Example(UserProblemSolved.class);
        exampleUserSolved.createCriteria().andEqualTo("username", username)
                .andEqualTo("problemId", problemId);
        // 查询解答题目表中的记录
        UserProblemSolved userProblemSolved = userProblemSolvedMapper.selectOneByExample(exampleUserSolved);
        // 如果解答表中没有先前的解答记录，代表是用户第一次答这题
        if (Objects.isNull(userProblemSolved)) {
            // 则题目ID的题目提交用户总数+1
            problemInfo.setTotalSubmitUser(problemInfo.getTotalSubmitUser() + 1);
            // 同时插入一条新的解答表记录
            UserProblemSolved newSolvedRecord = new UserProblemSolved();
            newSolvedRecord.setUsername(username);
            newSolvedRecord.setProblemId(problemId);
            newSolvedRecord.setTryCount(1);
            newSolvedRecord.setSolvedCount(0);
            newSolvedRecord.setLastTryTime(new Date());
            userProblemSolvedMapper.insertSelective(newSolvedRecord);
        } else {
            userProblemSolved.setTryCount(userProblemSolved.getTryCount() + 1);
            userProblemSolved.setLastTryTime(new Date());
            userProblemSolvedMapper.updateByExampleSelective(userProblemSolved, exampleUserSolved);

        }
        // 更新题目表
        problemInfoMapper.updateByExampleSelective(problemInfo, exampleProbInfo);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void ifLocalJudgeError(JudgeStatus judgeStatus) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        judgeStatus.setResult(StatusType.ERROR.getCode());
        // 更新评测记录中的评测结果为ERROR （提交失败）
        judgeStatusMapper.updateByPrimaryKeySelective(judgeStatus);
        // 更新评测结果内容
        JudgeResult judgeResult = new JudgeResult();
        judgeResult.setJudgeId(judgeStatus.getId());
        judgeResult.setInfo("本地评测机可能断开连接。失败于 " + dateFormat.format(new Date()));
        judgeResult.setTime(new Date());
        judgeResultMapper.insertSelective(judgeResult);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void ifSubmitError(JudgeStatus judgeStatus) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        judgeStatus.setResult(StatusType.ERROR.getCode());
        // 更新评测记录中的评测结果为ERROR （提交失败）
        judgeStatusMapper.updateByPrimaryKeySelective(judgeStatus);
        // 更新评测结果内容
        JudgeResult judgeResult = new JudgeResult();
        judgeResult.setJudgeId(judgeStatus.getId());
        judgeResult.setInfo("提交到本地评测机失败。失败于 " + dateFormat.format(new Date()));
        judgeResult.setTime(new Date());
        judgeResultMapper.insertSelective(judgeResult);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleJudgeResult(JudgeResultData judgeResultData) {
        // 根据rid获取测评信息
        JudgeStatus judgeStatus = judgeStatusMapper.selectByPrimaryKey(Integer.parseInt(judgeResultData.getRid()));
        // 将测评结果插入数据库
        JudgeResult judgeResult = new JudgeResult();
        judgeResult.setJudgeId(Integer.parseInt(judgeResultData.getRid()));
        judgeResult.setInfo(judgeResultData.getResultInfo());
        judgeResult.setTime(new Date());
        judgeResultMapper.insertSelective(judgeResult);


        // 返回结果为编译错误
        if(judgeResultData.getResult().equals(StatusType.CE.getName())){
            judgeStatus.setResult(StatusType.CE.getCode());
            judgeStatusMapper.updateByPrimaryKeySelective(judgeStatus);
        }
        // 编译成功了，但不一定是ac了。
        else{
            // 将时间测评结果 时间/空间消耗 刷进数据库
            judgeStatus.setResult(StatusType.getCodeByName(judgeResultData.getResult()));
            judgeStatus.setTimeUsed(judgeResultData.getTime());
            judgeStatus.setMemoryUsed(judgeResultData.getMemory());
            judgeStatusMapper.updateByPrimaryKeySelective(judgeStatus);


            // 刷新相关的信息
            String username = judgeStatus.getUsername();
            Integer problemId = judgeStatus.getProblemId();
            Example exampleProbInfo = new Example(ProblemInfo.class);
            exampleProbInfo.createCriteria().andEqualTo("problemId", problemId);
            ProblemInfo currentProbInfo = problemInfoMapper.selectOneByExample(exampleProbInfo);

            if (judgeResultData.getResult().equals(StatusType.AC.getName())) {
                // 题目 AC 数量加一
                currentProbInfo.setTotalAc(currentProbInfo.getTotalAc() + 1);
                // 创建 题目解答表的检索条件
                Example exampleUserSolved = new Example(UserProblemSolved.class);
                exampleUserSolved.createCriteria().andEqualTo("username", username)
                        .andEqualTo("problemId", problemId);
                // 查询解答题目表中的记录
                UserProblemSolved userProblemSolved = userProblemSolvedMapper.selectOneByExample(exampleUserSolved);
                //如果用户第一次解决这题
                if (userProblemSolved.getSolvedCount() == 0) {
                    currentProbInfo.setTotalAcUser(currentProbInfo.getTotalAcUser() + 1);
                    userProblemSolved.setSolvedCount(1);
                    userProblemSolved.setFirstSolvedTime(new Date());
                    userBaseInfoMapper.updateACNumAddOne(username);
                }
                // 如果用户不是第一次解决了这题，前面已经解决过了
                else {
                    userProblemSolved.setSolvedCount(userProblemSolved.getSolvedCount() + 1);
                }
                userProblemSolvedMapper.updateByPrimaryKeySelective(userProblemSolved);
                problemInfoMapper.updateByPrimaryKeySelective(currentProbInfo);
            }
        }

    }


    @Override
    public List<StatusCountVO> selectCountByDay(int days) {
        return judgeStatusMapper.selectCountByDay(days);
    }


    @Override
    public JudgeStatusVO selectJudgeStatus(Integer id) {
        JudgeStatus judgeStatus = judgeStatusMapper.selectByPrimaryKey(id);
        JudgeStatusVO result = new JudgeStatusVO();
        result.setId(judgeStatus.getId());
        result.setCode(judgeStatus.getCode());
        result.setCodeLength(judgeStatus.getCodeLength());
        result.setLanguage(CodeLanguage.getNameByCode(judgeStatus.getLanguage()));
        result.setMemoryUsed(judgeStatus.getMemoryUsed());
        result.setTimeUsed(judgeStatus.getTimeUsed());
        Example example = new Example(UserCustomInfo.class);
        example.createCriteria().andEqualTo("username", judgeStatus.getUsername());
        String nickname = userCustomInfoMapper.selectOneByExample(example).getNickname();
        result.setNickname(nickname);
        result.setProblemId(judgeStatus.getProblemId());
        if (Objects.nonNull(judgeStatus.getScore())) {
            result.setResult(StatusType.getNameByCode(judgeStatus.getResult()) + " " + judgeStatus.getScore());
        } else {
            result.setResult(StatusType.getNameByCode(judgeStatus.getResult()));
        }
        result.setSubmitTime(judgeStatus.getSubmitTime());
        result.setUsername(judgeStatus.getUsername());
        return result;
    }

    @Override
    public Integer selectCountByUsername(String username) {
        return judgeStatusMapper.selectCountByUsername(username);
    }

    @Override
    public JudgeStatus selectJudgeById(Integer id) {
        Example example = new Example(JudgeStatus.class);
        example.createCriteria().andEqualTo("id", id);
        return judgeStatusMapper.selectOneByExample(example);
    }

    @Override
    public List<LanguageUsedNumVO> countLanguageUsed() {
        List<LanguageUsedNumVO> languageUsedNumVOS = judgeStatusMapper.selectCountByLanguage();
        for (LanguageUsedNumVO vo : languageUsedNumVOS) {
            vo.setLanguageName(CodeLanguage.getNameByCode(vo.getLanguage()));
        }
        return languageUsedNumVOS;
    }

}
